//
// stacktrace_UNIX.cpp
// libprotea
// Tools Library for the Protea Project / Functionality for managing stacktraces, specific to UNIX systems
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "OS_defines.h"
#ifdef OS_UNIX

#include "defines.h"
#include "stacktrace.h"
#include "Fields/fields.h"
#include "AppHandler/AppInterface.h"
#include "OSDep/dir.h"
#include "OSDep/files.h"
#include "OSDep/process.h"
#include "defns.h"
using namespace ProteaApp1;

#include <stdarg.h>


void XCProfilePoint( XCAppInterface* app, const char* info ) {
	long timing;
	tPerfCounters* temp = app->perf_counters;

	if (app->TraceFunctions() && temp) {
		gettimeofday( &temp->end, NULL );

		timeval tresult;
		tresult.tv_sec = temp->end.tv_sec - temp->start.tv_sec;
		tresult.tv_usec = temp->end.tv_usec - temp->start.tv_usec;
		if (tresult.tv_usec < 0) {
			tresult.tv_sec--;
			tresult.tv_usec += USECSPERSEC;
		};

		timing = tresult.tv_sec * USECSPERSEC + tresult.tv_usec;

		app->ToolsTrace( TRC_FUNCTIONS, TLV_HIGH, info, timing );
	};
};


void XCInitProfiler() {
};

////////////
//
// XCTiming
//
//
#ifdef PENTIUM

long XCTiming::freq = 0;

extern __inline__ unsigned long long int rdtsc()
{
     unsigned long long int x;
//     __asm__ volatile (".byte 0x0f, 0xA2;" );				// CPUID
     __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));		// RDTSC
     return x;
}

#endif



XCTiming::XCTiming() {
	// Initialize the frequency counter
#ifdef PENTIUM

	if (freq == 0) {
		unsigned long long start;
		unsigned long long end;
		unsigned long long counter;
		unsigned long long temp;

		timespec req;
		timespec rem;

		cout << "Calibrating processor speed..." << endl;

		req.tv_nsec = 2E8;
		req.tv_sec = 0;
		rem.tv_nsec = 0;
		rem.tv_sec = 0;

		start = rdtsc();
		if (nanosleep( &req, &rem )) {
			while (rem.tv_nsec) {
				req.tv_nsec -= rem.tv_nsec;
				nanosleep( &req, &rem );
			};
		};
		end = rdtsc();

		freq = end - start;
//		cout << "Timing: " << freq << " clock ticks"<< endl;
		if (req.tv_sec)
			freq = freq / (req.tv_sec * 1000000);
		if (req.tv_nsec)
			freq = freq / (req.tv_nsec / 1000);
		cout << "Processor speed ~" << freq << " Mhz" << endl;
		freq = freq * 1000000;
	};
#endif
};


void XCTiming::Start() {
#ifdef PENTIUM
		start = rdtsc();
#else
		gettimeofday( &start, NULL );
#endif
};


double XCTiming::GetStart() {
#ifdef PENTIUM
	return start;
#else
	return start.tv_sec + (start.tv_usec*USECSPERSEC);
#endif
};


double XCTiming::GetCurrent() {
	double timing;

#ifdef PENTIUM
	timing = rdtsc() - start;
	timing = timing / freq;
#else
	gettimeofday( &end, NULL );

	timeval tresult;
	tresult.tv_sec = end.tv_sec - start.tv_sec;
	tresult.tv_usec = end.tv_usec - start.tv_usec;
	if (tresult.tv_usec < 0) {
		tresult.tv_sec--;
		tresult.tv_usec += USECSPERSEC;
	};

	timing = (tresult.tv_sec * USECSPERSEC + tresult.tv_usec) / 1E3;
#endif

	return timing;
};

#endif // OS_UNIX
